package com.seguriapp.gwt.client;

import java.util.Date;
import java.util.List;

import javax.inject.Inject;

import net.customware.gwt.dispatch.client.DispatchAsync;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceController;
import com.google.gwt.place.shared.PlaceHistoryHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.seguriapp.alerts.gwt.client.place.AlertsListPlace;
import com.seguriapp.common.gwt.client.AbstractSeguriAppPresenter;
import com.seguriapp.common.gwt.client.SeguriAppCallback;
import com.seguriapp.common.gwt.client.SeguriAppClientSession;
import com.seguriapp.common.gwt.client.SeguriAppClientSession.Key;
import com.seguriapp.common.gwt.client.SeguriAppDisplay;
import com.seguriapp.common.gwt.client.UtilClientValidations;
import com.seguriapp.complaints.gwt.client.place.ComplaintsListPlace;
import com.seguriapp.crimeactivity.gwt.client.place.CrimeActivityPlace;
import com.seguriapp.entities.Alert;
import com.seguriapp.entities.MenuLink;
import com.seguriapp.entities.User;
import com.seguriapp.gwt.client.login.AlertReadEvent;
import com.seguriapp.gwt.client.login.AlertReadEventHandler;
import com.seguriapp.gwt.client.login.AlertReceivedEvent;
import com.seguriapp.gwt.client.login.ComplaintReadEvent;
import com.seguriapp.gwt.client.login.ComplaintReadEventHandler;
import com.seguriapp.gwt.client.login.ComplaintReceivedEvent;
import com.seguriapp.gwt.client.login.UserLoggedInEvent;
import com.seguriapp.gwt.client.login.UserLoggedInEventHandler;
import com.seguriapp.gwt.client.login.UserLoggedOffEvent;
import com.seguriapp.gwt.client.login.UserLoggedOffEventHandler;
import com.seguriapp.gwt.shared.GetInitialDataAction;
import com.seguriapp.gwt.shared.GetInitialDataResult;
import com.seguriapp.gwt.shared.LogOffUserAction;
import com.seguriapp.gwt.shared.LogOffUserResult;

public class PrincipalPagePresenter extends AbstractSeguriAppPresenter {

  private final static String TITLE = "SeguriApp";

  /** Representacion abstracta de la pagina principal. */
  public interface Display extends SeguriAppDisplay {

    MenuPresenter.Display getMenu();

    AcceptsOneWidget getPanelCentral();

    PrincipalHeaderPresenter.Display getCabecera();

    void setPresenter(PrincipalPagePresenter principalPagePresenter);

    void setLoggedInUserInfo(String username, Date lastDateLoggedIn, boolean administrator, List<MenuLink> menuList);

    void removeUserInformation();

  }

  private int alertsCount = 0;
  private int complaintsCount = 0;
  private final Display display;
  private final PlaceHistoryHandler placeHistoryHandler;
  private DialogBox alertsDialogBox;

  /**
   * La pagina principal es la unica construida usando Gin.
   * 
   * @param dispatchAsync
   *          El despachador para llamadas Ajax
   * @param eventBus
   *          El bus de eventos
   * @param placeController
   *          El controlador de navegacion
   * @param display
   *          Implementacion concreta de la vista
   */
  @Inject
  public PrincipalPagePresenter(final DispatchAsync dispatchAsync, final EventBus eventBus, final PlaceController placeController, final Display display, final PlaceHistoryHandler placeHistoryHandler) {
    super(dispatchAsync, eventBus, placeController);
    this.display = display;
    display.setPresenter(this);
    this.placeHistoryHandler = placeHistoryHandler;
  }

  public void start() {
    // Mostrar la pantalla principal.
    RootPanel.get().add(display.asWidget());
    loadInitialData();
    addAuthenticationEventHandlers();
  }

  private void loadInitialData() {
    final GetInitialDataAction action = new GetInitialDataAction();
    action.setRole("Unauthenticated");
    dispatchAsync.execute(action, new SeguriAppCallback<GetInitialDataResult>() {

      public void onSuccess(final GetInitialDataResult result) {
        final SeguriAppClientSession clientSession = SeguriAppClientSession.get();
        if (result.getUser() == null) {
          clientSession.put(Key.NoUserMenus, result.getMenuLinks(), true);
        } else {
          clientSession.put(Key.User, result.getUser(), true);
        }
        new PrincipalHeaderPresenter(dispatchAsync, eventBus, placeController, display.getCabecera(), new Date(), result.getUser()).start();
        new MenuPresenter(dispatchAsync, eventBus, placeController, display.getMenu(), result.getMenuLinks()).start();
        placeHistoryHandler.handleCurrentHistory();
        if (placeController.getWhere().equals(Place.NOWHERE)) {
          if (result.getUser() != null) {
            startCometdService();
            final Place place = new ComplaintsListPlace(String.valueOf(new Date().getTime()));
            placeController.goTo(place);
          } else {
            final Place place = new CrimeActivityPlace(String.valueOf(new Date().getTime()));
            placeController.goTo(place);
          }
        }
      }

    });

  }

  private void addAuthenticationEventHandlers() {
    eventBus.addHandler(UserLoggedInEvent.TYPE, new UserLoggedInEventHandler() {

      public void onUserLoggedIn(final UserLoggedInEvent event) {
        setLoggedInUserInfo(event.getUsername(), event.getLastDateLoggedIn(), event.isAdministrator(), event.getMenuList());
        startCometdService();
      }

    });
    eventBus.addHandler(UserLoggedOffEvent.TYPE, new UserLoggedOffEventHandler() {

      public void onUserLoggedOff(final UserLoggedOffEvent event) {
        logOffUser();
      }

    });
    eventBus.addHandler(AlertReadEvent.TYPE, new AlertReadEventHandler() {

      public void onAlertRead(final AlertReadEvent event) {
        initTitle();
      }

    });
    eventBus.addHandler(ComplaintReadEvent.TYPE, new ComplaintReadEventHandler() {

      public void onComplaintRead(final ComplaintReadEvent event) {
        complaintsCount = 0;
      }

    });
  }

  public AcceptsOneWidget getPanelCentral() {
    return display.getPanelCentral();
  }

  private void setLoggedInUserInfo(final String username, final Date lastDateLoggedIn, final boolean administrator, final List<MenuLink> menuList) {
    display.setLoggedInUserInfo(username, lastDateLoggedIn, administrator, menuList);
  }

  private void logOffUser() {
    dispatchAsync.execute(new LogOffUserAction(), new SeguriAppCallback<LogOffUserResult>() {

      public void onSuccess(final LogOffUserResult result) {
        display.removeUserInformation();
        loadInitialData();
        disconnetCometdService();
        final Place place = new CrimeActivityPlace(String.valueOf(new Date().getTime()));
        placeController.goTo(place);
      }
    });
  }

  private void startCometdService() {
    startAlertCometdService(this);
  }

  private void initTitle() {
    alertsCount = 0;
    Window.setTitle(TITLE);
  }

  private native void startAlertCometdService(final PrincipalPagePresenter thisPresenter) /*-{
    $wnd._newAlert = function(serverInfo) {
      console.debug(serverInfo.data.alert.id + "");
      thisPresenter.@com.seguriapp.gwt.client.PrincipalPagePresenter::onNewAlertRegistered(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)
        (serverInfo.data.alert.id + "", serverInfo.data.alert.latitude + "", serverInfo.data.alert.longtitude + ""
        , serverInfo.data.alert.names, serverInfo.data.alert.lastNames, serverInfo.data.alert.dni
        , serverInfo.data.alert.date, serverInfo.data.alert.phone, serverInfo.data.alert.entity.id + "");
    };

    $wnd._newComplaint = function(serverInfo) {
      console.debug(serverInfo.data.complaint.id + "");
      thisPresenter.@com.seguriapp.gwt.client.PrincipalPagePresenter::onNewComplaintRegistered(Ljava/lang/String;) (serverInfo.data.complaint.entity.id + "");
    };

    $wnd._connectionEstablished = function() {
      console.debug((new Date()).getTime() + "_connectionEstablished");
    }

    $wnd._connectionBroken = function() {
      console.debug((new Date()).getTime() + "_connectionBroken");
    }

    $wnd._connectionClosed = function() {
      console.debug((new Date()).getTime() + "_connectionClosed");
    }

    $wnd._metaHandshake = function (handshake) {
      if (handshake.successful === true) {
        cometd.subscribe('/broadcast/newAlert', $wnd._newAlert);
        cometd.subscribe('/broadcast/newComplaint', $wnd._newComplaint);
        console.debug("channel subscribed");
      }
    };

    $wnd._metaConnect = function(message) {
      var cometd = $wnd.$.cometd;
      if (cometd.isDisconnected()) {
        console.debug("cometd is offline, connection closes");
        _connected = false;
        _connectionClosed();
        return;
      }
  
      var wasConnected = _connected;
      _connected = message.successful === true;
      if (!wasConnected && _connected) {
        console.debug((new Date()).getTime()
            + "no estaba conectado, se establece conexion");
        _connectionEstablished();
      } else if (wasConnected && !_connected) {
        console.debug("estaba conectado, se rompe conexion");
        _connectionBroken();
      }
    };

    var cometd = $wnd.$.cometd;
    var cometURL = "http://seguriapp.inf.pucp.edu.pe/SeguriAppBackEnd/cometd"
    console.debug("se prepara conexion a la url: " + cometURL);
    cometd.configure({
      url : cometURL,
      logLevel : 'warn'
    });
    cometd.addListener('/meta/handshake', $wnd._metaHandshake);
    cometd.addListener('/meta/connect', $wnd._metaConnect);
    cometd.handshake();
  
  }-*/;

  private native void disconnetCometdService() /*-{
    var cometd = $wnd.$.cometd;
    cometd.disconnect();
  }-*/;

  private void onNewAlertRegistered(final String id, final String latitude, final String longtitude, final String names
      , final String lastNames, final String dni, final String date, final String phone, final String entityId) {
    final SeguriAppClientSession clientSession = SeguriAppClientSession.get();
    if (clientSession.get(Key.User) != null) {
      if (isUserFromReceivedEntity(clientSession, Long.valueOf(entityId))) {
        final Alert alert = new Alert();
        alert.setId(Long.valueOf(id));
        alert.setLatitude(Double.valueOf(latitude));
        alert.setLongitude(Double.valueOf(latitude));
        alert.setNames(names);
        alert.setLastNames(lastNames);
        alert.setDni(dni);
        alert.setPhone(phone);
        alert.setEntityId(Long.valueOf(entityId));
        final Date dateObject = date == null || date.isEmpty() ? null : UtilClientValidations.DATE_SERVICE_FORMAT.parse(date);
        alert.setDate(dateObject);
        alertsCount++;
        Window.setTitle(" (" + alertsCount + ") " + TITLE);
        eventBus.fireEvent(new AlertReceivedEvent(alert.getId(), alert, alertsCount));
        // TODO syles for the dialog and timer
        if (!(placeController.getWhere() instanceof AlertsListPlace)) {
          alertsDialogBox = createDialogBox();
          alertsDialogBox.show();
        }
      }
    }
  }

  private boolean isUserFromReceivedEntity(final SeguriAppClientSession clientSession, final Long entityId) {
    if (((User) clientSession.get(Key.User)).isAdministrator()) {
      return true;
    } 
    final List<Long> userEntitiesList = ((User) clientSession.get(Key.User)).getEntitiesList();
    for (final Long userEntityId : userEntitiesList) {
      if (userEntityId.equals(entityId)) {
        return true;
      }
    }
    return false;
  }

  private DialogBox createDialogBox() {
    final DialogBox dialogBox = new DialogBox();
    final VerticalPanel dialogContents = new VerticalPanel();
    dialogContents.setSpacing(4);
    dialogBox.setWidget(dialogContents);

    final HorizontalPanel dialogHorContents = new HorizontalPanel();
    dialogHorContents.setSpacing(4);
    final Label newAlertLabel = new Label("Nueva(s) Alerta(s)");
    final Image logo = new Image("resources/images/seguriapp_alert.png");
    logo.setAltText("Logo de alerta");
    dialogHorContents.add(logo);
    dialogHorContents.setCellVerticalAlignment(logo, HasVerticalAlignment.ALIGN_MIDDLE);
    dialogHorContents.add(newAlertLabel);
    dialogHorContents.setCellVerticalAlignment(newAlertLabel, HasVerticalAlignment.ALIGN_MIDDLE);
    final Anchor goToAlertsPageLink = new Anchor("Ver");
    goToAlertsPageLink.addClickHandler(new ClickHandler() {

      public void onClick(ClickEvent event) {
        dialogBox.hide();
        initTitle();
        goToAlertsListPage();
      }
    });

    final Anchor closeLink = new Anchor("Cerrar");
    closeLink.addClickHandler(new ClickHandler() {

      public void onClick(ClickEvent event) {
        dialogBox.hide();
        initTitle();
      }
    });

    dialogContents.add(closeLink);
    dialogContents.setCellHorizontalAlignment(closeLink, HasHorizontalAlignment.ALIGN_RIGHT);
    dialogContents.add(dialogHorContents);
    dialogContents.setCellHorizontalAlignment(dialogHorContents, HasHorizontalAlignment.ALIGN_CENTER);
    dialogContents.add(goToAlertsPageLink);
    dialogContents.setCellHorizontalAlignment(goToAlertsPageLink, HasHorizontalAlignment.ALIGN_RIGHT);
    dialogBox.setModal(false);
    dialogBox.setPopupPosition(0, 40);

    dialogBox.setGlassEnabled(false);
    dialogBox.setAutoHideEnabled(false);
    dialogBox.setAnimationEnabled(false);
    return dialogBox;
  }

//  private void goToAlertDetailsPage(final Alert alert) {
//    final SeguriAppClientSession clientSession = SeguriAppClientSession.get();
//    clientSession.put(SeguriAppClientSession.Key.Alert, alert, true);
//    final Place place = new AlertDetailsPlace("");
//    placeController.goTo(place);
//  }

  private void goToAlertsListPage() {
    final Place place = new AlertsListPlace("");
    placeController.goTo(place);
  }

  private String getValueFromJsonObject(String json, final String name) {
    if (json.indexOf(name + ":") < 0) {
      return null;
    }
    json = json.substring(json.indexOf(name + ":") + name.length() + 2);
    return json.substring(0, json.indexOf(","));
  }

  private void onNewComplaintRegistered(final String entityId) {
    final SeguriAppClientSession clientSession = SeguriAppClientSession.get();
    if (clientSession.get(Key.User) != null) {
      if (isUserFromReceivedEntity(clientSession, Long.valueOf(entityId))) {
        complaintsCount++;
        eventBus.fireEvent(new ComplaintReceivedEvent(complaintsCount));
      }
    }
  }

}
